package pl.enigmatic.math;

import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.Vector;


public class LayeredMap<K, V> implements Map<K, V> {

	private final Map<K, V> upper;
	private final Map<K, V> lower;

	public LayeredMap(final Map<K, V> upper, final Map<K, V> lower) {
		this.upper = upper;
		this.lower = lower;
	}

	public LayeredMap() {
		this(new HashMap<K, V>(), new HashMap<K, V>());
	}

	@Override
	public void clear() {
		upper.clear();
		lower.clear();
	}

	@Override
	public boolean containsKey(final Object key) {
		return upper.containsKey(key) || lower.containsKey(key);
	}

	@Override
	public boolean containsValue(final Object value) {
		return upper.containsValue(value) || lower.containsValue(value);
	}

	@Override
	public Set<Map.Entry<K, V>> entrySet() {
		final Set<Map.Entry<K, V>> set = new HashSet<Map.Entry<K, V>>();
		set.addAll(upper.entrySet());
		for (final Map.Entry<K, V> e : lower.entrySet()) {
			if (!upper.containsKey(e.getKey())) {
				set.add(e);
			}
		}
		return set;
	}

	@Override
	public V get(final Object key) {
		if (!upper.containsKey(key)) { return lower.get(key); }
		return upper.get(key);
	}

	@Override
	public boolean isEmpty() {
		return upper.isEmpty() && lower.isEmpty();
	}

	@Override
	public Set<K> keySet() {
		final Set<K> set = new HashSet<K>();
		set.addAll(upper.keySet());
		set.addAll(lower.keySet());
		return set;
	}

	@Override
	public V put(final K key, final V value) {
		return upper.put(key, value);
	}

	@Override
	public void putAll(final Map<? extends K, ? extends V> m) {
		upper.putAll(m);
	}

	@Override
	public V remove(final Object key) {
		if (!upper.containsKey(key)) { return lower.remove(key); }
		lower.remove(key);
		return upper.remove(key);
	}

	@Override
	public int size() {
		return keySet().size();
	}

	@Override
	public Collection<V> values() {
		final Collection<V> set = new Vector<V>();
		set.addAll(upper.values());
		set.addAll(lower.values());
		return set;
	}

	public Map<K, V> getUpper() {
		return upper;
	}

	public Map<K, V> getLower() {
		return lower;
	}
}
